package test.client.executor;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import test.client.analyzer.Analyzer;
import test.client.analyzer.DefaultAnalyzer;
import test.client.support.Watcher;
import test.client.support.balancer.RoundRobinLoadBalancer;
import test.client.support.balancer.LoadBalancer;
import test.client.support.thread.DefaultThreadManager;
import test.client.support.thread.ThreadListener;
import test.client.support.thread.ThreadManager;
import test.client.thread.ClientThread;
import test.configuration.ConfigFactory;
import test.configuration.Configuration;
import test.domain.environment.MetaThread;
import test.generator.UserGenerator;

public abstract class Client {
	protected Log log = LogFactory.getLog(this.getClass());
	
	private ThreadManager threadManager;
	private ThreadListener threadListener;
	private LoadBalancer balancer;
	
	private Analyzer analyzer;
	private UserGenerator generator;
	
	private boolean initialized = false;

	public void initialize(int maxThreadCount) {
		Configuration config = ConfigFactory.getConfiguration();
		MetaThread meta = config.getMetaThread();
		
		if (maxThreadCount > 0) meta.setMaxThreadCount(maxThreadCount);
		
		DefaultThreadManager manager = new DefaultThreadManager();
		manager.initialize(meta);
		
		threadManager = manager;
		threadListener = manager;
		
		// default load balancer is RoundRobinLoadBalancer
		balancer = new RoundRobinLoadBalancer().setServers(config.getServers());
		
		analyzer = new DefaultAnalyzer()
						.setThreadManager(threadManager)
						.setResultFile(config.getResultFile());
		
		generator = new UserGenerator();
		
		this.initialized = true;
	}
	
	public boolean isPrepared() {
		return initialized && threadManager.isInitialized(); 
	}
	
	public void prepare(ClientThread client, int index) {
		this.prepare(client, index, true);
	}
	
	public void prepare(ClientThread client, int index, boolean managed) {
		client.setId(index);
		client.setServer(balancer.getServer());
		client.setThreadListener(threadListener);
		client.setUsername(generator.getUsername(String.valueOf(index)));
		client.setPassword(generator.getPassword());
		
		if (managed) threadManager.put(client);
	}
	
	public void start() {
		if (!this.isPrepared()) 
			throw new IllegalStateException("Client is not prepared.");
		
		this.threadManager.start();
	}
	
	public void stop() {
		this.threadManager.stop();
	}
	
	public void startWatcher() {
		if (!this.initialized) 
			throw new IllegalStateException("Client is not initialized.");
		
		Watcher watcher = new Watcher(analyzer, threadManager);
//		new Thread(watcher).start();
//		log.info("Watcher is started.");
		
		watcher.run();
	}	
	
	public ThreadManager getThreadManager() {
		return threadManager;
	}

	public Analyzer getAnalyzer() {
		return analyzer;
	}

	// Execute the test
	public abstract void execute();
}
